/*
 * Agari NLN
 * 
 * TCSS 305 Winter 2011 Assignment 5 Tetris 19 February 2011
 */

package tetris.pieces;

import java.awt.Color;
import java.awt.Point;

/**
 * A class to represent Tetris pieces in general.
 * 
 * @author Agari NLN (agarin@uw.edu)
 * @version 1.0
 */
public abstract class AbstractShape implements Cloneable
{
  /**
   * The number of rows in the array where the shapes are being represented.
   */
  public static final int ROW = 4;

  /**
   * The number of columns in the array where the shapes are being represented.
   */
  public static final int COLUMN = 4;

  /**
   * This is to set the default orientation to North.
   */
  public static final Orientation DEFAULT_ORIENTATION = Orientation.North;

  /**
   * This is to represent my [], has a name for better readability.
   */
  public static final String MY_BLOCK = "[]";

  /**
   * This is to represent my spaces, has a name for better readability.
   */
  public static final String MY_NO_BLOCK = "  ";

  /**
   * The tetris piece that is currently selected represented by a boolean array.
   */
  protected boolean[][] my_shape = new boolean[COLUMN][ROW];

  /**
   * The tetris piece that is at the north orientation, represented by a boolean
   * array.
   */
  protected boolean[][] my_north_shape = new boolean[COLUMN][ROW];

  /**
   * The tetris piece that is at the south orientation, represented by a boolean
   * array.
   */
  protected boolean[][] my_south_shape = new boolean[COLUMN][ROW];

  /**
   * The tetris piece that is at the east orientation, represented by a boolean
   * array.
   */
  protected boolean[][] my_east_shape = new boolean[COLUMN][ROW];

  /**
   * The tetris piece that is at the west orientation, represented by a boolean
   * array.
   */
  protected boolean[][] my_west_shape = new boolean[COLUMN][ROW];
  
  /**
   * The color of this shape.
   */
  protected Color my_color;

  /**
   * The default position where the piece will be drawn on the board.
   */
  private Point my_board_position;

  /**
   * The orientation that is in use.
   */
  private Orientation my_orientation;
  

  /**
   * An enumeration for all the possible positions of my shapes(blocks).
   * 
   * @author Agari NLN (agarin@uw.edu)
   * @version 1.0
   */
  public enum Orientation
  {
    /**
     * The default and front facing one is defined as North.
     */
    North,
    /**
     * The opposite of the north, as if the shape has turned 180 degrees.
     */
    South,
    /**
     * The shape it will be when rotated to right once is defined as east.
     */
    East,
    /**
     * The shape it will be when rotated to left once is defined as west.
     */
    West
  }

  /**
   * Constructs an abstract shape with given position and default orientation.
   * 
   * @param the_position the position relative to any board(default set to 0,0 at places).
   */
  public AbstractShape(final Point the_position)
  {
    this(the_position, DEFAULT_ORIENTATION);
  }
  
  /**
   * Constructs an abstract shape with given position and orientation.
   * 
   * @param the_position the position relative to any board(default set to 0,0 at places).
   * @param the_orientation The beginning orientation of this piece
   */
  public AbstractShape(final Point the_position, final Orientation the_orientation)
  {
    my_orientation = the_orientation;

    switch (my_orientation)
    {
      case North:
        my_shape = my_north_shape;
        break;
      case East:
        my_shape = my_east_shape;
        break;
      case West:
        my_shape = my_west_shape;
        break;
      case South:
        my_shape = my_south_shape;
        break;
      default:
        my_shape = my_north_shape;
        break;
    }
    
    my_board_position = the_position;
  }
  
  @Override
  public String toString()
  {
    final StringBuilder string = new StringBuilder();

    for (int y = 0; y < ROW; y++)
    {
      for (int x = 0; x < COLUMN; x++)
      {
        if (my_shape[x][y])
        {
          string.append(MY_BLOCK);
        }
        else
        {
          string.append(MY_NO_BLOCK);
        }
      }

      string.append("\n");
    }

    return string.toString();
  }

  /**
   * The method to rotate my shape to the right once.
   */
  public void rotateRight()
  {
    switch (my_orientation)
    {
      case North:
        my_orientation = Orientation.East;
        my_shape = my_east_shape;
        break;
      case East:
        my_orientation = Orientation.South;
        my_shape = my_south_shape;
        break;
      case West:
        my_orientation = Orientation.North;
        my_shape = my_north_shape;
        break;
      case South:
        my_orientation = Orientation.West;
        my_shape = my_west_shape;
        break;
      default:
        // this case should never be hit since we have
        // only four orientations, however, just to be
        // safe, let's set it to north
        my_orientation = Orientation.North;
        my_shape = my_north_shape;
        break;
    }
  }

  /**
   * The method to rotate my shape to the left once.
   */
  public void rotateLeft()
  {
    switch (my_orientation)
    {
      case North:
        my_orientation = Orientation.West;
        my_shape = my_west_shape;
        break;
      case East:
        my_orientation = Orientation.North;
        my_shape = my_north_shape;
        break;
      case West:
        my_orientation = Orientation.South;
        my_shape = my_south_shape;
        break;
      case South:
        my_orientation = Orientation.East;
        my_shape = my_east_shape;
        break;
      default:
        // this case should never be hit since we have
        // only four orientations, however, just to be
        // safe, let's set it to north
        my_orientation = Orientation.North;
        my_shape = my_north_shape;
        break;
    }
  }

  /**
   * The method to move my shape to the left once.
   */
  public void moveLeft()
  {
    my_board_position.setLocation(my_board_position.x - 1, my_board_position.y);

  }

  /**
   * The method to move my shape to the right once.
   */
  public void moveRight()
  {

    my_board_position.setLocation(my_board_position.x + 1, my_board_position.y);

  }

  /**
   * The method to move my shape down once.
   */
  public void moveDown()
  {
    my_board_position.setLocation(my_board_position.x, my_board_position.y + 1);
  }

  /**
   * This method will return my current position relative to the board.
   * 
   * @return my position relative to the board.
   */
  public Point myPosition()
  {
    return my_board_position;
  }
  
  /**
   * Gets the positions of blocks which make up this piece relative
   * to the board origin.
   * 
   * @return An array of points representing the coordinates of this shape
   */
  public Point[] blockPositions()
  {
    final Point [] arraypoints = new Point[ROW];
    int temp = 0;
    
    for (int y = 0; y < ROW; y++)
    {
      for (int x = 0; x < COLUMN; x++)
      {
        if (my_shape[x][y])
        {
          arraypoints[temp] = new Point(x + my_board_position.x, y  + my_board_position.y);
          temp++;
        }
      }
    }
    
    return arraypoints;
  }
  
  /**
   * Gets the positions of blocks independent of the board origin.
   * 
   * @return An array of points representing the coordinates of this shape
   */
  public Point[] blockPositionsNoBoard()
  {
    final Point [] arraypoints = new Point[ROW];
    int temp = 0;
    
    for (int y = 0; y < ROW; y++)
    {
      for (int x = 0; x < COLUMN; x++)
      {
        if (my_shape[x][y])
        {
          arraypoints[temp] = new Point(x , y);
          temp++;
        }
      }
    }
    
    return arraypoints;
  }
  
  /**
   * Gets the color of this shape.
   * @return Color of this shape
   */
  public Color getColor()
  {
    return my_color;
  }
}
